package com.example.apidocserver.infrastructure.util.document;

import com.alibaba.druid.util.StringUtils;
import com.example.apidocserver.domain.document.entity.ApiInfo;
import com.example.apidocserver.domain.document.entity.ApiParam;
import com.example.apidocserver.domain.document.entity.ApiParamField;
import com.example.apidocserver.domain.document.entity.ApiParamHis;
import com.example.apidocserver.infrastructure.constants.SystemConstants;
import com.example.apidocserver.interfaces.vo.ApiInfoAllVo;
import com.example.apidocserver.interfaces.vo.ApiParamDescVo;
import org.apache.commons.lang3.StringEscapeUtils;
import org.springframework.util.CollectionUtils;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.example.apidocserver.infrastructure.constants.DocToYuqueConstants.*;


/**
 * 实体对象转word工具
 *
 * @author xyqlg
 */
public class EntityToYuque {
    /**
     * 设置接口信息
     *
     * @param sb      文档信息
     * @param apiInfo 接口信息
     */
    public static void setApiInfo(StringBuilder sb, ApiInfo apiInfo, int startTitle) {
        setTitle(sb, apiInfo.getApiName(), startTitle);
        setTitle(sb, "接口信息", startTitle + 1);
        addDesc(sb, apiInfo.getApiDesc());
        setTitle(sb, "接口信息", startTitle + 2);
        sb.append(apiInfo.getApiType()).append("&nbsp;&nbsp;&nbsp;").append(apiInfo.getApiUri()).append("\n");
        setTitle(sb, "服务信息", startTitle + 2);
        sb.append(CODE_START).append(toCode(apiInfo.getApiHeader())).append(CODE_END);
    }

    /**
     * 设置参数说明
     *
     * @param sb             文档信息
     * @param apiParamDescVo 参数说明
     */
    public static void setParamDesc(StringBuilder sb, ApiParamDescVo apiParamDescVo, int startTitle) {
        setTitle(sb, "参数&字段说明", startTitle + 1);
        setTitle(sb, "入参参数", startTitle + 2);
        setNotBodyParamDesc(sb, "入参头参数说明", apiParamDescVo.getInputHeader(), startTitle, INPUT);
        setNotBodyParamDesc(sb, "入参路径参数说明", apiParamDescVo.getInputPath(), startTitle, INPUT);
        setNotBodyParamDesc(sb, "入参查询参数说明", apiParamDescVo.getInputQuery(), startTitle, INPUT);
        setBodyParamDesc(sb, "入参体参数说明", apiParamDescVo.getInputBody(), startTitle, INPUT);

        setTitle(sb, "返回参数", startTitle + 2);
        setNotBodyParamDesc(sb, "返回头参数说明", apiParamDescVo.getOutputHeader(), startTitle, OUTPUT);
        setBodyParamDesc(sb, "返回体参数说明", apiParamDescVo.getOutputBody(), startTitle, OUTPUT);
    }

    /**
     * 设置参数示例
     *
     * @param sb              文档信息
     * @param currentParamHis 参数示例
     */
    public static void setParamHis(StringBuilder sb, ApiParamHis currentParamHis, String titleStr, int startTitle) {
        setTitle(sb, titleStr, startTitle + 1);
        setTitle(sb, "入参参数示例", startTitle + 2, INPUT);
        setInputHis(sb, currentParamHis, startTitle);

        setTitle(sb, "返回参数示例", startTitle + 2, OUTPUT);
        setOutputHis(sb, currentParamHis, startTitle);
    }

    public static void setTitle(StringBuilder sb, String title, int titleNo) {
        setTitle(sb, title, titleNo, COMMON);
    }

    public static void setTitle(StringBuilder sb, String title, int titleNo, String type) {
        String start = "";
        if (titleNo < 7) {
            if (INPUT.equals(type)) {
                start = H_START_INPUT_ARR[titleNo];
            } else if (OUTPUT.equals(type)) {
                start = H_START_OUTPUT_ARR[titleNo];
            } else {
                start = H_START_ARR[titleNo];
            }
            sb.append(start).append(toHtml(title)).append(H_END[titleNo]);
        } else {
            if (INPUT.equals(type)) {
                start = P_STRONG_START_INPUT;
            } else if (OUTPUT.equals(type)) {
                start = P_STRONG_START_OUTPUT;
            } else {
                start = P_STRONG_START;
            }
            sb.append(start).append(toHtml(title)).append(P_STRONG_END);
        }
    }

    private static String toHtml(String title) {
        if (StringUtils.isEmpty(title)) {
            return title;
        }
        return StringEscapeUtils.escapeHtml4(title);
    }

    public static String toYuqueStr(ApiInfoAllVo apiInfoAllVo, int startTitle) {
        StringBuilder sb = new StringBuilder();
        setApiInfo(sb, apiInfoAllVo.getApiInfo(), startTitle);
        setParamDesc(sb, apiInfoAllVo.getApiParamDescVo(), startTitle);

        if (!CollectionUtils.isEmpty(apiInfoAllVo.getApiParamHisList())) {
            setTitle(sb, "参数示例", startTitle + 1);
            for (int i = 0; i < apiInfoAllVo.getApiParamHisList().size(); i++) {
                ApiParamHis apiParamHis = apiInfoAllVo.getApiParamHisList().get(i);
                if (Objects.nonNull(apiParamHis)) {
                    setParamHis(sb, apiParamHis, apiParamHis.getParamName(), startTitle + 1);
                }
            }
        }

        return sb.toString();
    }

    private static void setInputHis(StringBuilder sb, ApiParamHis currentParamHis, int startTitle) {
        setHisStr(sb, "入参头参数示例", currentParamHis.getParamInputHeaderStr(), startTitle, INPUT);
        setHisStr(sb, "入参路径参数示例", currentParamHis.getParamInputPathStr(), startTitle, INPUT);
        setHisStr(sb, "入参查询参数示例", currentParamHis.getParamInputQueryStr(), startTitle, INPUT);
        setHisStr(sb, "入参体参数示例", currentParamHis.getParamInputBodyStr(), startTitle, INPUT);
    }

    private static void setOutputHis(StringBuilder sb, ApiParamHis currentParamHis, int startTitle) {
        setHisStr(sb, "返回头参数示例", currentParamHis.getParamOutputHeaderStr(), startTitle, OUTPUT);
        setHisStr(sb, "返回体参数示例", currentParamHis.getParamOutputBodyStr(), startTitle, OUTPUT);
    }

    private static void setHisStr(StringBuilder sb, String title, String paramOutputBodyStr, int startTitle,
                                  String type) {
        if (StringUtils.isEmpty(paramOutputBodyStr)) {
            return;
        }
        setTitle(sb, title, startTitle + 3, type);
        sb.append(CODE_START).append(toCode(paramOutputBodyStr)).append(CODE_END);
    }

    private static List<Character> list = Stream.of('(', ')', '!').collect(Collectors.toList());

    private static String toCode(String str) {
        if (StringUtils.isEmpty(str)) {
            return str;
        }
        StringBuilder sb = new StringBuilder();
        for (char c : str.toCharArray()) {
            if (c == '\"') {
                sb.append("%5C%22");
            } else if (c == '\n') {
                sb.append("%5Cn");
            } else if (c == ' ') {
                sb.append("%20");
            } else if (c == '\\') {
                sb.append("%5C%5C");
            } else if (list.contains(c)) {
                sb.append(c);
            } else {
                try {
                    sb.append(URLEncoder.encode(Character.toString(c), String.valueOf(StandardCharsets.UTF_8)));
                } catch (UnsupportedEncodingException e) {
                    throw new RuntimeException();
                }
            }
        }
        return sb.toString();
    }

    private static void setBodyParamDesc(StringBuilder sb, String title, List<ApiParam> inputBody, int startTitle,
                                         String type) {
        if (CollectionUtils.isEmpty(inputBody)) {
            return;
        }
        setTitle(sb, title, startTitle + 3, type);
        for (ApiParam apiParam : inputBody) {
            if (CollectionUtils.isEmpty(apiParam.getFields())) {
                continue;
            }
            sb.append(apiParam.getTitle()).append("\n");
            setFieldTable(sb, apiParam);
        }
    }

    private static void setNotBodyParamDesc(StringBuilder sb, String title, ApiParam param, int startTitle,
                                            String type) {
        if (Objects.isNull(param) || CollectionUtils.isEmpty(param.getFields())) {
            return;
        }
        setTitle(sb, title, startTitle + 3, type);
        setFieldTable(sb, param);
    }

    private static void setFieldTable(StringBuilder sb, ApiParam param) {
        if (Objects.isNull(param) || CollectionUtils.isEmpty(param.getFields())) {
            return;
        }
        addDesc(sb, param.getParamDesc());
        sb.append(TABLE_START);
        for (ApiParamField field : param.getFields()) {
            setField(sb, field);
        }
        sb.append(TABLE_END);
    }

    private static void setField(StringBuilder sb, ApiParamField field) {
        String filedTypeStr = EntityToWord.ID_AND_NAME_TYPE_MAP.get(field.getFieldTypeId());
        filedTypeStr = StringUtils.isEmpty(filedTypeStr) ? String.valueOf(field.getFieldTypeId()) : filedTypeStr;

        sb.append(TR_START);
        addTD(sb, field.getFieldName(), field.getFieldMust());
        addTD(sb, filedTypeStr, field.getFieldMust());
        addTD(sb, field.getFieldMust(), field.getFieldMust());
        addFieldDesc(sb, field.getFieldDesc());
        sb.append(TR_END);
    }

    private static void addTD(StringBuilder sb, String txtIn, String must) {
        String txt = toHtml(txtIn);
        if (SystemConstants.STR_YES.equals(must)) {
            sb.append(TD_STRONG_START).append(txt).append(TD_STRONG_END);
        } else {
            sb.append(TD_START).append(txt).append(TD_END);
        }
    }

    private static void addFieldDesc(StringBuilder sb, String fieldDesc) {
        String txt = toHtml(fieldDesc).replaceAll("\n", "<br>");
        sb.append(TD_START).append(txt).append(TD_END);
    }

    private static void addDesc(StringBuilder sb, String txt) {
        if (SystemConstants.NO_DATA.equals(txt)) {
            return;
        }
        sb.append(P_START).append(txt).append(P_END);
    }
}
